/**
 * ypdisk.h -- New member training, step 4
 *
 * Author: yangpeng
 **/

#include <linux/fs.h> 
#include <linux/spinlock.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/bio.h> 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/hdreg.h> 
#include <linux/sched.h> 
#include <linux/kernel.h>
#include <linux/slab.h> 
#include <linux/fs.h> 
#include <linux/errno.h> 
#include <linux/types.h> 
#include <linux/hdreg.h> 
#include <linux/kdev_t.h> 
#include <linux/vmalloc.h> 
#include <linux/radix-tree.h>
#include <asm/semaphore.h>
#include <linux/kthread.h> 
#include <linux/err.h>   
#include <linux/jiffies.h>


/*
 * define PDEBUG for debug
 */
#undef PDEBUG                   
#ifdef YPDISK_DEBUG                                      
#       define PDEBUG(fmt, args...) printk( KERN_ERR "ypdisk: " fmt, ## args)
#else                                                       
#       define PDEBUG(fmt, args...) 
#endif 

#define YPDISK_MAJOR 0
#define YPDISK_MINORS 16

#define KERNEL_SECTOR_SIZE 512
/* Number of sectors 16M */
#define NSECTORS (64 * 64 * (4096 / 512))

/*
 * macro for writeback
 */
#define DIRTY_THRESHOLD 40
#define WORK_RATE (DIRTY_THRESHOLD >> 1)
#define WORK_NUM (WORK_RATE + (WORK_RATE >> 1))
#define INTERVAL 20

/*
 * marcor for writeback
 * LOW_PATH: the path of low device
 * DISK_OFFSET: before this sector is label and bitmap
 */
#define LOW_PATH "/dev/vg0/5g0"
#define DISK_OFFSET 9

/*
 * structure for yp_comm_disk level
 * bio_data: used for bio->bi_private of function
 *           disk_read, create_dbmp, save_dbmp_last           
 * write_bio_data: used for bio->bi_private of fucntion
 *            disk_read
 */
struct yp_comm_disk {
	struct block_device * low_bdev;
	wait_queue_head_t read_queue;
        wait_queue_head_t dbmp_queue;
};
struct bio_data {
	wait_queue_head_t * queue;
        int flag;
	int done;
	int err;
};
struct write_bio_data {
        struct tree_node * p;
};

/* 
 * structure for yp_mem
 * tree_node: the leaf node of radix tree
 * dirty_list: the node of dirty list
 */
struct yp_mem {	
	struct radix_tree_root ypdisk_tree;
	struct semaphore *sem_tree;
	spinlock_t  page_lock;
	wait_queue_head_t page_queue;

	struct list_head d_list;
	spinlock_t list_lock;

	char * label;
	struct page * label_page;
	char * disk_bmp;
	struct page * dbmp_page;

	atomic_t d_num;
	atomic_t w_rate;

	int size;
};
struct tree_node {
	int d_flag;
        int is_locked;
	char page_bits;
	struct page * page_p;
        spinlock_t * page_lock;
        wait_queue_head_t * page_queue;
};
struct dirty_list {
	struct list_head list;
	int index;
	unsigned long timestamp;
	struct tree_node * tree_p;
};

/*
 * data structure for yp_writeback
 */
struct yp_writeback {
	struct work_struct * wb_work;
	struct task_struct * wb_thread;
	unsigned long interval;

};

/*
 * data structure for yp_bio_exc
 */
struct yp_bio_exc {
	struct request_queue * queue;
	struct gendisk * gd;

};

/*
  Declare
 */
static inline int yp_tests_dbmp(struct yp_mem *mm, int sector);
static void write_dirty_pages(struct yp_mem *mm, int type); 
static inline int yp_get_treenode(struct tree_node * node);
static inline void yp_put_treenode(struct tree_node * node);

